/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.oss.api.endpoint;

import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.elsfs.cloud.oss.api.OssTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * oss 控制器
 *
 * @author zeng
 */
public class OssController {

  /** aws 对外提供服务端点 */
  @RestController
  @RequestMapping("/oss")
  @ConditionalOnProperty(name = "file.oss.enable", havingValue = "true")
  @ConditionalOnBean(OssTemplate.class)
  @RequiredArgsConstructor
  @Tag(name = "oss", description = "文件")
  public static class OssEndpoint {

    private final OssTemplate template;

    /**
     * 创建 bucket
     *
     * @param bucketName 桶名字
     * @return 桶信息
     */
    @PostMapping("/bucket/{bucketName}")
    @Operation(summary = "创建bucket", description = "创建bucket")
    public Bucket createBucket(
        @PathVariable("bucketName") @Schema(description = "桶名称") String bucketName) {
      template.createBucket(bucketName);
      return template.getBucket(bucketName).get();
    }

    /**
     * 获取桶列表
     *
     * @return 桶列表
     */
    @GetMapping("/bucket")
    @Operation(summary = "获取桶列表", description = "获取桶列表")
    public List<Bucket> getBuckets() {
      return template.getAllBuckets();
    }

    /**
     * 获取桶信息
     *
     * @param bucketName 桶名字
     * @return 桶信息
     */
    @GetMapping("/bucket/{bucketName}")
    @Operation(summary = "获取桶信息", description = "获取桶信息")
    public Bucket getBucket(
        @PathVariable("bucketName") @Schema(description = "桶名称") String bucketName) {
      return template
          .getBucket(bucketName)
          .orElseThrow(() -> new IllegalArgumentException("Bucket Name not found!"));
    }

    /**
     * 删除桶
     *
     * @param bucketName 桶名称
     */
    @DeleteMapping("/bucket/{bucketName}")
    @ResponseStatus(HttpStatus.ACCEPTED)
    @Operation(summary = "删除桶", description = "删除桶")
    public void deleteBucket(
        @PathVariable("bucketName") @Schema(description = "桶名称") String bucketName) {
      template.removeBucket(bucketName);
    }

    /**
     * 上传文件
     *
     * @param object 文档
     * @param bucketName 桶名称
     * @return 文件信息
     */
    @PostMapping("/object/{bucketName}")
    @Operation(summary = "上传文件", description = "上传文件")
    public S3Object createObject(
        @RequestParam("file") MultipartFile object,
        @Schema(description = "桶名称") @PathVariable("bucketName") String bucketName) {
      String name = object.getOriginalFilename();
      InputStream inputStream = null;
      try {
        inputStream = object.getInputStream();
        PutObjectResult result =
            template.putObject(
                bucketName, name, inputStream, object.getSize(), object.getContentType());
        S3Object info = template.getObjectInfo(bucketName, name);
        return info;
      } catch (Exception e) {
        throw new RuntimeException(e);
      } finally {
        try {
          if (inputStream != null) {
            inputStream.close();
          }
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }

    /**
     * 上传
     *
     * @param object 文件
     * @param bucketName 桶名称
     * @param objectName 文件名称
     * @return sso
     */
    @SneakyThrows
    @PostMapping("/object/{bucketName}/{objectName}")
    @Operation(summary = "上传文件", description = "上传文件")
    public S3Object createObject(
        @RequestParam("file") MultipartFile object,
        @Schema(description = "桶名称") @PathVariable("bucketName") String bucketName,
        @Schema(description = "文件名称") @PathVariable("objectName") String objectName) {
      @Cleanup InputStream inputStream = object.getInputStream();
      template.putObject(
          bucketName, objectName, inputStream, object.getSize(), object.getContentType());
      return template.getObjectInfo(bucketName, objectName);
    }

    /**
     * 根据存储桶名称和对象名称过滤S3对象。
     *
     * @param bucketName S3存储桶的名称，通过路径变量传递。
     * @param objectName S3对象的名称，也是通过路径变量传递，用于指定对象名称的前缀进行过滤。
     * @return 返回一个包含所有匹配对象概要的列表。概要包括对象的基本信息，如名称、大小等。
     */
    @GetMapping("/object/{bucketName}/{objectName}")
    @Operation(summary = "根据存储桶名称和对象名称过滤S3对象", description = "根据存储桶名称和对象名称过滤S3对象")
    public List<S3ObjectSummary> filterObject(
        @Schema(description = "桶名称") @PathVariable("bucketName") String bucketName,
        @Schema(description = "文件名称") @PathVariable("objectName") String objectName) {
      // 通过存储桶名称和对象名称前缀获取所有对象的概要信息，包括匹配的子目录中的对象。
      return template.getAllObjectsByPrefix(bucketName, objectName, true);
    }

    /**
     * 获取文件信息
     *
     * @param bucketName 桶名称
     * @param objectName 文件名称
     * @param expires 过期时间
     * @return map
     */
    @GetMapping("/object/{bucketName}/{objectName}/{expires}")
    @Operation(summary = "获取文件信息", description = "获取文件信息")
    public Map<String, Object> getObject(
        @Schema(description = "过期时间") @PathVariable("bucketName") String bucketName,
        @Schema(description = "文件名称") @PathVariable("objectName") String objectName,
        @Schema(description = "过期时间") @PathVariable("expires") Integer expires) {
      Map<String, Object> responseBody = new HashMap<>(8);
      // Put Object info
      responseBody.put("bucket", bucketName);
      responseBody.put("object", objectName);
      responseBody.put("url", template.getObjectURL(bucketName, objectName, expires));
      responseBody.put("expires", expires);
      return responseBody;
    }

    /**
     * 删除指定存储桶中的对象。
     *
     * @param bucketName 存储桶的名称，对应URL路径中的{bucketName}。
     * @param objectName 对象的名称，对应URL路径中的{objectName}。
     */
    @ResponseStatus(HttpStatus.ACCEPTED)
    @DeleteMapping("/object/{bucketName}/{objectName}/")
    @Operation(summary = "删除指定存储桶中的对象", description = "删除指定存储桶中的对象")
    public void deleteObject(
        @Schema(description = "桶名称") @PathVariable("bucketName") String bucketName,
        @Schema(description = "文件名称") @PathVariable("objectName") String objectName) {
      // 通过模板删除指定存储桶中的对象
      template.removeObject(bucketName, objectName);
    }
  }
}
